package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io/fs"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"go-file-perception-model/internal/config"
	"go-file-perception-model/internal/model"
	"go-file-perception-model/internal/repository"
	"go-file-perception-model/internal/service"
)

// Tool descriptor for tools/list
func ToolSearchFilenamesDescriptor() Tool {
	return Tool{
		Name:        "search_filenames",
		Description: "搜索文件名, 按名称(支持正则/子串)搜索文件和目录；参数可选，未提供参数时返回已配置索引目录的树状结构。返回结果为统一的文本描述，以树状结构展示目录和文件关系，便于大模型理解文件结构。",
		InputSchema: map[string]any{
			"type": "object",
			"properties": map[string]any{
				"pattern": map[string]any{"type": "string", "description": "搜索表达式(可选)，支持正则或子串"},
				"directories": map[string]any{"type": "array", "items": map[string]any{"type": "string"}, "description": "限制检索目录(可选)。相对路径会相对索引目录补全为绝对路径"},
				"limit": map[string]any{"type": "integer", "default": 100, "description": "最大结果数(搜索时有效)"},
			},
			// 所有参数均为可选
		},
	}
} // <--- Added closing brace here

// openFileMonitorRepo opens the SQLite DB used by the web service at ./data/file_monitor.db
func openFileMonitorRepo() (repository.FileMonitorRepository, error) {
    cwd, err := os.Getwd()
    if err != nil {
        return nil, err
    }
    dbPath := filepath.Join(cwd, "data", "file_monitor.db")
    return repository.NewFileMonitorRepository(dbPath)
}

// Non-stream call handler
func CallSearchFilenames(args map[string]any) (any, error) {
	pattern, _ := args["pattern"].(string)
	userDirs := extractStringArray(args["directories"])

	// Determine directories and search path
	resolvedDirs, err := resolveDirectories(userDirs)
	if err != nil { return nil, err }
	var searchPath string
	if len(resolvedDirs) > 0 { searchPath = resolvedDirs[0] }

	// Try DB-backed search; if any errors happen, fallback to filesystem
	var resp *model.DirectoryTreeResponse
	if cfg, err := config.LoadConfig(); err == nil {
		if repo, err2 := openFileMonitorRepo(); err2 == nil {
			defer repo.Close()
			treeSvc := service.NewDBDirectoryTreeService(repo, cfg)
			if r, err3 := treeSvc.SearchFilesInDatabase(model.DBDirectoryTreeSearchRequest{ Pattern: pattern, SearchPath: searchPath }); err3 == nil {
				resp = r
			}
		}
	}

	// Shape output to content + summary; if DB empty, fallback to filesystem tree for index dirs
	if resp != nil && resp.Root != nil {
		// 生成统一的目录结构文本描述
		treeDescription := generateTreeTextDescription(resp.Root, "")
		
		// 创建符合MCP标准的内容对象
		contentObj := map[string]any{
			"type": "text",
			"text": treeDescription,
			"metadata": map[string]any{
				"total_files":    resp.TotalFiles,
				"total_dirs":     resp.TotalDirs,
				"indexed_files":  resp.IndexedFiles,
				"filtered_files": resp.FilteredFiles,
				"took_ms":        resp.Took,
			},
		}
		
		summary := map[string]any{ "total_files": resp.TotalFiles, "total_dirs": resp.TotalDirs, "indexed_files": resp.IndexedFiles, "filtered_files": resp.FilteredFiles, "took_ms": resp.Took }
		return map[string]any{"content": []map[string]any{contentObj}, "summary": summary}, nil
	}
	// Fallback: build FS trees from resolved index dirs so users still see structure
	fsContent, sums, err := buildDirectoryTrees(resolvedDirs, 2000)
	if err != nil { return nil, err }
	
	// 生成统一的目录结构文本描述
	var treeDescription strings.Builder
	for _, node := range fsContent {
		treeDescription.WriteString(generateTreeTextDescription(node, ""))
	}
	
	// 创建符合MCP标准的内容对象
	contentObj := map[string]any{
		"type": "text",
		"text": treeDescription.String(),
		"metadata": map[string]any{
			"tree_nodes": sums,
		},
	}
	
	return map[string]any{"content": []map[string]any{contentObj}, "summary": sums}, nil
}

// Stream call handler
func StreamSearchFilenames(ctx context.Context, args map[string]any, enc *json.Encoder, flusher http.Flusher) error {
	pattern, _ := args["pattern"].(string)
	userDirs := extractStringArray(args["directories"])

	resolvedDirs, err := resolveDirectories(userDirs)
	if err != nil { return err }
	var searchPath string
	if len(resolvedDirs) > 0 { searchPath = resolvedDirs[0] }

	// Try DB-backed search first
	var resp *model.DirectoryTreeResponse
	if cfg, err := config.LoadConfig(); err == nil {
		if repo, err2 := openFileMonitorRepo(); err2 == nil {
			defer repo.Close()
			treeSvc := service.NewDBDirectoryTreeService(repo, cfg)
			if r, err3 := treeSvc.SearchFilesInDatabase(model.DBDirectoryTreeSearchRequest{ Pattern: pattern, SearchPath: searchPath }); err3 == nil {
				resp = r
			}
		}
	}
	if resp != nil && resp.Root != nil {
		// 生成统一的目录结构文本描述
		treeDescription := generateTreeTextDescription(resp.Root, "")
		
		// 创建符合MCP标准的内容对象
		contentObj := map[string]any{
			"type": "text",
			"text": treeDescription,
			"metadata": map[string]any{
				"total_files":    resp.TotalFiles,
				"total_dirs":     resp.TotalDirs,
				"indexed_files":  resp.IndexedFiles,
				"filtered_files": resp.FilteredFiles,
				"took_ms":        resp.Took,
			},
		}
		
		// 输出内容对象
		_ = enc.Encode(map[string]any{"event": "item", "data": contentObj})
		flusher.Flush()
		
		// 输出摘要信息
		_ = enc.Encode(map[string]any{"event": "item", "data": map[string]any{"summary": map[string]any{ "total_files": resp.TotalFiles, "total_dirs": resp.TotalDirs, "indexed_files": resp.IndexedFiles, "filtered_files": resp.FilteredFiles, "took_ms": resp.Took }}})
		flusher.Flush()
		return nil
	}
	// Fallback: stream FS trees
	content, sums, err := buildDirectoryTrees(resolvedDirs, 2000)
	if err != nil { return err }
	
	// 生成统一的目录结构文本描述
	var treeDescription strings.Builder
	for _, node := range content {
		treeDescription.WriteString(generateTreeTextDescription(node, ""))
	}
	
	// 创建符合MCP标准的内容对象
	contentObj := map[string]any{
		"type": "text",
		"text": treeDescription.String(),
		"metadata": map[string]any{
			"tree_nodes": sums,
		},
	}
	
	// 输出内容对象
	_ = enc.Encode(map[string]any{"event": "item", "data": contentObj})
	flusher.Flush()
	
	// 输出摘要信息
	_ = enc.Encode(map[string]any{"event": "item", "data": map[string]any{"summary": sums}})
	flusher.Flush()
	return nil
}

// Shared search routine (non-stream)
func performFilenameSearch(pattern string, directories []string, limit int) ([]map[string]any, error) {
	results := make([]map[string]any, 0, limit)
	count := 0
	
	// 获取配置的索引目录
	var indexDirectory string
	if cfg, err := config.LoadConfig(); err == nil && len(cfg.FileIndex.IndexDirectories) > 0 {
		indexDirectory = cfg.FileIndex.IndexDirectories[0]
	}
	
	for _, root := range directories {
		if count >= limit { break }
		_ = filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
			if err != nil { return nil }
			if count >= limit { return fs.SkipAll }
			if matchByPattern(path, pattern) {
				info, statErr := os.Stat(path)
				if statErr == nil {
					results = append(results, map[string]any{
						"name": filepath.Base(path),
						"path": getRelativePath(path, indexDirectory),
						"size": info.Size(),
						"ext":  filepath.Ext(path),
					})
					count++
				}
			}
			return nil
		})
	}
	return results, nil
}

func matchByPattern(path, pattern string) bool {
	if pattern == "" { return true }
	// try regex first
	if re, err := regexp.Compile(pattern); err == nil {
		return re.MatchString(path) || re.MatchString(filepath.Base(path))
	}
	p := strings.ToLower(pattern)
	return strings.Contains(strings.ToLower(path), p) || strings.Contains(strings.ToLower(filepath.Base(path)), p)
}

// Helpers
func extractStringArray(v any) []string {
	var out []string
	if arr, ok := v.([]any); ok {
		for _, d := range arr {
			if s, ok2 := d.(string); ok2 && s != "" {
				out = append(out, s)
			}
		}
	}
	return out
}

func resolveDirectories(userDirs []string) ([]string, error) {
	// Load configured index directories
	cfg, err := config.LoadConfig()
	if err != nil { return nil, err }
	indexDirs := cfg.FileIndex.IndexDirectories

	// default
	if len(userDirs) == 0 {
		if len(indexDirs) > 0 { return indexDirs, nil }
		return []string{"."}, nil
	}

	// resolve each user dir
	resultSet := make(map[string]struct{})
	add := func(p string) { if p != "" { if abs, err := filepath.Abs(p); err == nil { resultSet[abs] = struct{}{} } } }

	for _, d := range userDirs {
		if filepath.IsAbs(d) {
			add(d)
			continue
		}
		// relative -> resolve against each index dir
		if len(indexDirs) == 0 { add(d); continue }
		for _, base := range indexDirs {
			add(filepath.Join(base, d))
		}
	}
	out := make([]string, 0, len(resultSet))
	for p := range resultSet { out = append(out, p) }
	return out, nil
}

// Tree structures
type TreeNode struct {
	Name     string      `json:"name"`
	Path     string      `json:"path"`
	Type     string      `json:"type"` // "directory" | "file"
	Size     int64       `json:"size,omitempty"`
	Children []*TreeNode `json:"children,omitempty"`
}

func buildDirectoryTrees(roots []string, maxNodes int) ([]*TreeNode, map[string]any, error) {
	var result []*TreeNode
	totalNodes := 0
	dirCount := 0
	fileCount := 0
	
	// 获取配置的索引目录
	var indexDirectory string
	if cfg, err := config.LoadConfig(); err == nil && len(cfg.FileIndex.IndexDirectories) > 0 {
		indexDirectory = cfg.FileIndex.IndexDirectories[0]
	}
	
	for _, r := range roots {
		if totalNodes >= maxNodes { break }
		node := &TreeNode{Name: filepath.Base(r), Path: getRelativePath(r, indexDirectory), Type: "directory"}
		err := filepath.WalkDir(r, func(path string, d fs.DirEntry, err error) error {
			if err != nil { return nil }
			if path == r { totalNodes++; dirCount++; return nil }
			if totalNodes >= maxNodes { return fs.SkipAll }
			rel, _ := filepath.Rel(r, path)
			parts := splitPath(rel)
			insertNode(node, r, parts, d, indexDirectory)
			totalNodes++
			if d.IsDir() { dirCount++ } else { fileCount++ }
			return nil
		})
		if err != nil { return nil, nil, err }
		result = append(result, node)
	}
	return result, map[string]any{"total_nodes": totalNodes, "directories": dirCount, "files": fileCount}, nil
}

func buildTreeFromMatches(matches []map[string]any) []*TreeNode {
	// 获取配置的索引目录
	var indexDirectory string
	if cfg, err := config.LoadConfig(); err == nil && len(cfg.FileIndex.IndexDirectories) > 0 {
		indexDirectory = cfg.FileIndex.IndexDirectories[0]
	}
	
	// group by root path prefix (drive or first dir)
	roots := make(map[string]*TreeNode)
	for _, m := range matches {
		p, _ := m["path"].(string)
		if p == "" { continue }
		rootKey := rootOfPath(p)
		root := roots[rootKey]
		if root == nil {
			root = &TreeNode{Name: filepath.Base(rootKey), Path: getRelativePath(rootKey, indexDirectory), Type: "directory"}
			roots[rootKey] = root
		}
		// insert file node
		// find relative from rootKey
		rel := strings.TrimPrefix(p, rootKey)
		rel = strings.TrimPrefix(rel, string(os.PathSeparator))
		parts := splitPath(rel)
		insertNode(root, rootKey, parts, nil, indexDirectory)
	}
	out := make([]*TreeNode, 0, len(roots))
	for _, n := range roots { out = append(out, n) }
	return out
}

func insertNode(root *TreeNode, rootPath string, parts []string, d fs.DirEntry, indexDirectory string) {
	cur := root
	curPath := rootPath
	for i, part := range parts {
		curPath = filepath.Join(curPath, part)
		isLast := i == len(parts)-1
		// find or create child
		var child *TreeNode
		for _, c := range cur.Children { if c.Name == part { child = c; break } }
		if child == nil {
			t := "directory"
			var size int64
			if isLast && d != nil && !d.IsDir() { 
				t = "file"
				if info, err := d.Info(); err == nil {
					size = info.Size()
				}
			}
			child = &TreeNode{Name: part, Path: getRelativePath(curPath, indexDirectory), Type: t, Size: size}
			cur.Children = append(cur.Children, child)
		}
		cur = child
	}
}

// getRelativePath 获取相对于索引目录的相对路径
func getRelativePath(absolutePath, indexDirectory string) string {
	if indexDirectory == "" {
		return absolutePath
	}
	
	// 计算相对路径
	relativePath, err := filepath.Rel(indexDirectory, absolutePath)
	if err != nil {
		// 如果计算相对路径失败，返回原始路径
		return absolutePath
	}
	
	// 如果相对路径以 ".." 开头，说明文件不在索引目录内，返回原始路径
	if strings.HasPrefix(relativePath, "..") {
		return absolutePath
	}
	
	return relativePath
}

func splitPath(rel string) []string {
	if rel == "" { return nil }
	parts := strings.Split(rel, string(os.PathSeparator))
	out := make([]string, 0, len(parts))
	for _, p := range parts { if p != "" { out = append(out, p) } }
	return out
}

func rootOfPath(p string) string {
	// On Windows, keep volume (e.g., C:\). On *nix, use first path segment from root
	vol := filepath.VolumeName(p)
	if vol != "" {
		// ensure trailing backslash
		rest := strings.TrimPrefix(p, vol)
		if rest == "" || strings.HasPrefix(rest, "\\") || strings.HasPrefix(rest, "/") {
			return vol
		}
		return vol
	}
	// unix-like
	if strings.HasPrefix(p, string(os.PathSeparator)) { return string(os.PathSeparator) }
	// relative; take first segment
	seg := p
	if i := strings.IndexRune(p, os.PathSeparator); i >= 0 { seg = p[:i] }
	return seg
}

// generateTreeTextDescription 生成统一的目录结构文本描述
func generateTreeTextDescription(node interface{}, indent string) string {
	if node == nil {
		return ""
	}
	
	var name, nodeType string
	var size int64
	var children interface{}
	
	// 根据实际类型提取字段
	switch n := node.(type) {
	case *TreeNode:
		name = n.Name
		nodeType = n.Type
		size = n.Size
		children = n.Children
	case *model.DirectoryTreeNode:
		name = n.Name
		nodeType = n.Type
		size = n.Size
		children = n.Children
	default:
		return ""
	}
	
	// 构建当前节点的文本描述
	var textBuilder strings.Builder
	if nodeType == "directory" {
		textBuilder.WriteString(fmt.Sprintf("%s📁 %s/ (目录)\n", indent, name))
	} else {
		// 格式化文件大小
		var sizeStr string
		if size < 1024 {
			sizeStr = fmt.Sprintf("%dB", size)
		} else if size < 1024*1024 {
			sizeStr = fmt.Sprintf("%.1fKB", float64(size)/1024)
		} else {
			sizeStr = fmt.Sprintf("%.1fMB", float64(size)/(1024*1024))
		}
		textBuilder.WriteString(fmt.Sprintf("%s📄 %s (文件, %s)\n", indent, name, sizeStr))
	}
	
	// 递归处理子节点
	if childs, ok := children.([]*TreeNode); ok {
		for _, child := range childs {
			textBuilder.WriteString(generateTreeTextDescription(child, indent+"  "))
		}
	} else if childs, ok := children.([]*model.DirectoryTreeNode); ok {
		for _, child := range childs {
			textBuilder.WriteString(generateTreeTextDescription(child, indent+"  "))
		}
	}
	
	return textBuilder.String()
}

// convertTreeNodeToContent 将TreeNode结构体转换为符合MCP标准的内容对象
func convertTreeNodeToContent(node interface{}, results *[]map[string]any) {
	if node == nil {
		return
	}
	
	var name, path, nodeType string
	var size int64
	var children interface{}
	
	// 根据实际类型提取字段
	switch n := node.(type) {
	case *TreeNode:
		name = n.Name
		path = n.Path
		nodeType = n.Type
		size = n.Size
		children = n.Children
	case *model.DirectoryTreeNode:
		name = n.Name
		path = n.Path
		nodeType = n.Type
		size = n.Size
		children = n.Children
	default:
		return
	}
	
	// 创建文本内容表示
	var textContent string
	if nodeType == "directory" {
		var childCount int
		if childs, ok := children.([]*TreeNode); ok {
			childCount = len(childs)
		} else if childs, ok := children.([]*model.DirectoryTreeNode); ok {
			childCount = len(childs)
		}
		textContent = fmt.Sprintf("目录: %s\n路径: %s\n子项数: %d", name, path, childCount)
	} else {
		textContent = fmt.Sprintf("文件: %s\n路径: %s\n大小: %d字节", name, path, size)
	}
	
	// 创建符合MCP标准的内容对象
	contentObj := map[string]any{
		"type": "text",
		"text": textContent,
		"metadata": map[string]any{
			"name":     name,
			"path":     path,
			"type":     nodeType,
			"size":     size,
			"children": children,
		},
	}
	
	// 添加到结果列表
	*results = append(*results, contentObj)
	
	// 递归处理子节点
	if childs, ok := children.([]*TreeNode); ok {
		for _, child := range childs {
			convertTreeNodeToContent(child, results)
		}
	} else if childs, ok := children.([]*model.DirectoryTreeNode); ok {
		for _, child := range childs {
			convertTreeNodeToContent(child, results)
		}
	}
}
